package vfs.obj.entity.impl;

import java.sql.Connection;
import vfs.except.VFSException;
import vfs.filter.VFSFilter;
import vfs.obj.entity.VFSEntityFactory;
import vfs.obj.entity.VFSNodeType;
import vfs.obj.entity.VFSGroup;
import vfs.obj.entity.VFSNode;
import vfs.obj.entity.VFSNode.PublicAccess;
import vfs.obj.entity.VFSUser;
import vfs.persist.entity.manage.VFSEntityManager;
import vfs.persist.entity.build.impl.VFSNodeTypeBuilder;
import vfs.persist.entity.build.impl.VFSGroupBuilder;
import vfs.persist.entity.build.impl.VFSNodeBuilder;
import vfs.persist.entity.build.impl.VFSUserBuilder;
import vfs.persist.entity.manage.impl.VFSEntityManagerImpl;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import vfs.obj.association.VFSAssociationFactory;
import vfs.obj.association.VFSNodeToNodeType;
import vfs.obj.association.VFSPath;
import vfs.obj.association.impl.VFSAssociationFactoryImpl;
import vfs.obj.entity.VFSPathAncestor;
import vfs.obj.entity.VFSPathDescendant;
import vfs.util.VFSPersistUtil;

/**
 *
 * @author Matthew Eavenson <durandal@uga.edu>
 */
public class VFSEntityFactoryImpl implements VFSEntityFactory{
    
    private Connection conn;
    private VFSAssociationFactory aFactory;
    private VFSEntityManager<VFSUser> userManager;
    private VFSEntityManager<VFSGroup> groupManager;
    private VFSEntityManager<VFSNodeType> nodeTypeManager;
    private VFSEntityManager<VFSNode> nodeManager;
    private VFSNodeToNodeType nft;
    private VFSPath path;
    
    public VFSEntityFactoryImpl(Connection conn){
        this.conn = conn;
        aFactory = new VFSAssociationFactoryImpl(conn);
        nft = aFactory.getNodeToNodeType();
        path = aFactory.getPath();
        userManager = new VFSEntityManagerImpl<VFSUser>(conn, new VFSUserBuilder());
        groupManager = new VFSEntityManagerImpl<VFSGroup>(conn, new VFSGroupBuilder());
        nodeTypeManager = new VFSEntityManagerImpl<VFSNodeType>(conn, new VFSNodeTypeBuilder());
        nodeManager = new VFSEntityManagerImpl<VFSNode>(conn, new VFSNodeBuilder());
    }
    
    // add a new user
    @Override
    public VFSUser createUser(String name) throws VFSException{
        VFSUser user = new VFSUserImpl(name);
        userManager.add(user);
        return user;
    }

    // add a new group
    @Override
    public VFSGroup createGroup(String name) throws VFSException{
        VFSGroup group = new VFSGroupImpl(name);
        groupManager.add(group);
        return group;
    }

    // add a new file type
    @Override
    public VFSNodeType createNodeType(String name, String description) throws VFSException{
        VFSNodeType nodeType = new VFSNodeTypeImpl(name, description);
        nodeTypeManager.add(nodeType);
        return nodeType;
    }

    // add a new node
    @Override
    public VFSNode createNode(String name, String location, VFSNode.PublicAccess access, VFSUser owner, VFSNode parent) throws VFSException{
        
        // parent node must be a folder
        if(nft.getNodeType(parent) != null)
            throw new VFSException("Destination is not a folder");
        
        Date now = new Date();
        VFSNode node = new VFSNodeImpl(name, location, now, now, access);
        
        Map<String, String> foreignKeys = new HashMap<String, String>();
        foreignKeys.put("ownedBy", owner.getId()+"");
        foreignKeys.put("parent", parent.getId()+"");
        
        // begin transaction
        VFSPersistUtil.disableAutoCommit(conn);
        //long start = System.currentTimeMillis();
        nodeManager.add(node, foreignKeys);
        //System.out.println("adding node took "+(System.currentTimeMillis()-start)+"ms");
        // add path information
        
        //start = System.currentTimeMillis();
        Iterator<VFSPathAncestor> ancestors = path.getAncestors(parent, null);
        //System.out.println("query path took "+(System.currentTimeMillis()-start)+"ms");
        
        //start = System.currentTimeMillis();
        while( ancestors.hasNext() ){
            VFSPathAncestor ancestor = ancestors.next();
            path.addPath(ancestor.getAncestor(), node, ancestor.length()+1);
        }
        path.addPath(parent, node, 1);
        //System.out.println("adding path took "+(System.currentTimeMillis()-start)+"ms");
        
        // end transaction
        VFSPersistUtil.commit(conn);
        // reenable autocommit
        VFSPersistUtil.enableAutoCommit(conn);
        
        return node;
    }

    @Override
    public void removeUser(VFSFilter filter) throws VFSException {
        userManager.remove(filter);
    }

    @Override
    public void removeGroup(VFSFilter filter) throws VFSException {
        groupManager.remove(filter);
    }

    @Override
    public void removeNodeType(VFSFilter filter) throws VFSException {
        nodeTypeManager.remove(filter);
    }

    @Override
    public void removeNode(VFSFilter filter) throws VFSException {        
        for(Iterator<VFSNode> nodes = nodeManager.get(filter); nodes.hasNext();){
            VFSNode node = nodes.next();
            removePath(node);
        }
    }
    
    @Override
    public void removeUser(VFSUser user) throws VFSException {
        userManager.remove(user);
    }

    @Override
    public void removeGroup(VFSGroup group) throws VFSException {
        groupManager.remove(group);
    }

    @Override
    public void removeNodeType(VFSNodeType nodeType) throws VFSException {
        nodeTypeManager.remove(nodeType);
    }

    @Override
    public void removeNode(VFSNode node) throws VFSException {
        removePath(node);
    }
    
    private void removePath(VFSNode node) throws VFSException {
        
        // begin transaction
        VFSPersistUtil.disableAutoCommit(conn);
        
        for(Iterator<VFSPathDescendant> descendants = path.getDescendants(node, null); descendants.hasNext();){
            VFSNode descendant = descendants.next().getDescendant();
            path.removeAll(descendant);
            nodeManager.remove(descendant);
        }
        path.removeAll(node);
        nodeManager.remove(node);
        
        // end transaction
        VFSPersistUtil.commit(conn);
        // reenable autocommit
        VFSPersistUtil.enableAutoCommit(conn);
    }
    
}
